home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / Shells / tcsh / Source / sh.dir.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-21  |  30.1 KB  |  1,316 lines

  1. /* $Header: /u/christos/src/tcsh-6.03/RCS/sh.dir.c,v 3.20 1992/10/10 18:17:34 christos Exp $ */
  2. /*
  3.  * sh.dir.c: Directory manipulation functions
  4.  */
  5. /*-
  6.  * Copyright (c) 1980, 1991 The Regents of the University of California.
  7.  * All rights reserved.
  8.  *
  9.  * Redistribution and use in source and binary forms, with or without
  10.  * modification, are permitted provided that the following conditions
  11.  * are met:
  12.  * 1. Redistributions of source code must retain the above copyright
  13.  *    notice, this list of conditions and the following disclaimer.
  14.  * 2. Redistributions in binary form must reproduce the above copyright
  15.  *    notice, this list of conditions and the following disclaimer in the
  16.  *    documentation and/or other materials provided with the distribution.
  17.  * 3. All advertising materials mentioning features or use of this software
  18.  *    must display the following acknowledgement:
  19.  *    This product includes software developed by the University of
  20.  *    California, Berkeley and its contributors.
  21.  * 4. Neither the name of the University nor the names of its contributors
  22.  *    may be used to endorse or promote products derived from this software
  23.  *    without specific prior written permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  26.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  29.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  30.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  31.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  34.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  35.  * SUCH DAMAGE.
  36.  */
  37. #include "sh.h"
  38.  
  39. RCSID("$Id: sh.dir.c,v 3.20 1992/10/10 18:17:34 christos Exp $")
  40.  
  41. /*
  42.  * C Shell - directory management
  43.  */
  44.  
  45. static    struct directory    *dfind        __P((Char *));
  46. static    Char             *dfollow    __P((Char *));
  47. static    void                printdirs    __P((int));
  48. static    Char             *dgoto        __P((Char *));
  49. static    void                dnewcwd    __P((struct directory *, int));
  50. static    void                dset        __P((Char *));
  51. static  void              dextract    __P((struct directory *));
  52. static  int              skipargs    __P((Char ***, char *, char *));
  53. static    void             dgetstack    __P((void));
  54.  
  55. static struct directory dhead;        /* "head" of loop */
  56. static int    printd;            /* force name to be printed */
  57. static Char   olddir[MAXPATHLEN+1];    /* old directory */
  58.  
  59. int     bequiet = 0;        /* do not print dir stack -strike */
  60.  
  61. /*
  62.  * dinit - initialize current working directory
  63.  */
  64. void
  65. dinit(hp)
  66.     Char   *hp;
  67. {
  68.     register char *tcp;
  69.     register Char *cp;
  70.     register struct directory *dp;
  71.     char    path[MAXPATHLEN];
  72.     static char *emsg = "tcsh: Trying to start from \"%s\"\n";
  73.  
  74.     /* Don't believe the login shell home, because it may be a symlink */
  75.     tcp = (char *) getwd(path);
  76.     if (tcp == NULL || *tcp == '\0') {
  77.     xprintf("tcsh: %s\n", path);
  78.     if (hp && *hp) {
  79.         tcp = short2str(hp);
  80.         xprintf(emsg, tcp);
  81.         if (chdir(tcp) == -1)
  82.         cp = NULL;
  83.         else
  84.         cp = Strsave(hp);
  85.     }
  86.     else
  87.         cp = NULL;
  88.     if (cp == NULL) {
  89.         xprintf(emsg, "/");
  90.         if (chdir("/") == -1)
  91.         /* I am not even try to print an error message! */
  92.         xexit(1);
  93.         cp = SAVE("/");
  94.     }
  95.     }
  96.     else {
  97. #ifdef S_IFLNK
  98.     struct stat swd, shp;
  99.  
  100.     /*
  101.      * See if $HOME is the working directory we got and use that
  102.      */
  103.     if (hp && *hp &&
  104.         stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
  105.         DEV_DEV_COMPARE(swd.st_dev, shp.st_dev)  &&
  106.         swd.st_ino == shp.st_ino)
  107.         cp = Strsave(hp);
  108.     else {
  109.         char   *cwd;
  110.  
  111.         /*
  112.          * use PWD if we have it (for subshells)
  113.          */
  114.         if ((cwd = getenv("PWD")) != NULL) {
  115.         if (stat(cwd, &shp) != -1 && 
  116.             DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) &&
  117.             swd.st_ino == shp.st_ino)
  118.             tcp = cwd;
  119.         }
  120.         cp = dcanon(SAVE(tcp), STRNULL);
  121.     }
  122. #else /* S_IFLNK */
  123.     cp = dcanon(SAVE(tcp), STRNULL);
  124. #endif /* S_IFLNK */
  125.     }
  126.  
  127.     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  128.     dp->di_name = cp;
  129.     dp->di_count = 0;
  130.     dhead.di_next = dhead.di_prev = dp;
  131.     dp->di_next = dp->di_prev = &dhead;
  132.     printd = 0;
  133.     dnewcwd(dp, 0);
  134. }
  135.  
  136. static void
  137. dset(dp)
  138. Char *dp;
  139. {
  140.     /*
  141.      * Don't call set() directly cause if the directory contains ` or
  142.      * other junk characters glob will fail. 
  143.      */
  144.     register Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **)));
  145.  
  146.     vec[0] = Strsave(dp);
  147.     vec[1] = 0;
  148.     (void) Strcpy(olddir, value(STRcwd));
  149.     setq(STRcwd, vec, &shvhed);
  150.     tsetenv(STRPWD, dp);
  151. }
  152.  
  153. #define DIR_LONG      0x01
  154. #define DIR_VERT      0x02
  155. #define DIR_LINE      0x04
  156. #define DIR_SAVE     0x08
  157. #define DIR_LOAD    0x10
  158. #define DIR_CLEAR    0x20
  159. #define DIR_OLD          0x40
  160.  
  161. static int
  162. skipargs(v, dstr, str)
  163.     Char ***v;
  164.     char   *dstr;
  165.     char   *str;
  166. {
  167.     Char  **n = *v, *s;
  168.  
  169.     int dflag = 0;
  170.     for (n++; *n != NULL && (*n)[0] == '-'; n++) 
  171.     if (*(s = &((*n)[1])) == '\0')
  172.         dflag |= DIR_OLD;
  173.     else {
  174.         char *p;
  175.         if ((p = strchr(dstr, *s)) != NULL)
  176.         dflag |= (1 << (p - dstr));
  177.         else {
  178.         stderror(ERR_DIRUS, short2str(**v), dstr, str);
  179.         break;
  180.         }
  181.     }
  182.     if (*n && (dflag & DIR_OLD))
  183.     stderror(ERR_DIRUS, short2str(**v), dstr, str);
  184.     *v = n;
  185.     return dflag;
  186. }
  187.  
  188. /*
  189.  * dodirs - list all directories in directory loop
  190.  */
  191. /*ARGSUSED*/
  192. void
  193. dodirs(v, c)
  194.     Char  **v;
  195.     struct command *c;
  196. {
  197.     int dflag = skipargs(&v, "lvnSLc", "");
  198.  
  199.     if ((dflag & DIR_CLEAR) != 0) {
  200.     struct directory *dp, *fdp;
  201.     for (dp = dcwd->di_next; dp != dcwd; ) {
  202.         fdp = dp;
  203.         dp = dp->di_next;
  204.         if (fdp != &dhead)
  205.         dfree(fdp);
  206.     }
  207.     dhead.di_next = dhead.di_prev = dp;
  208.     dp->di_next = dp->di_prev = &dhead;
  209.     }
  210.     if ((dflag & DIR_LOAD) != 0) 
  211.     loaddirs(*v++);
  212.     else if ((dflag & DIR_SAVE) != 0)
  213.     recdirs(*v++);
  214.  
  215.     if (*v != NULL || (dflag & DIR_OLD))
  216.     stderror(ERR_DIRUS, "dirs", "lvnSLc", "");
  217.     if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0)
  218.     printdirs(dflag);
  219. }
  220.  
  221. static void
  222. printdirs(dflag)
  223.     int dflag;
  224. {
  225.     register struct directory *dp;
  226.     Char   *s, *hp = value(STRhome);
  227.     int     idx, len, cur;
  228.     extern int T_Cols;
  229.  
  230.     if (*hp == '\0')
  231.     hp = NULL;
  232.     dp = dcwd;
  233.     idx = 0;
  234.     cur = 0;
  235.     do {
  236.     if (dp == &dhead)
  237.         continue;
  238.     if (dflag & DIR_VERT) {
  239.         xprintf("%d\t", idx++);
  240.         cur = 0;
  241.     }
  242.     len = Strlen(hp);
  243.     if (!(dflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) &&
  244.         Strncmp(hp, dp->di_name, len) == 0 &&
  245.         (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) 
  246.         len = Strlen(s = (dp->di_name + len)) + 2;
  247.     else
  248.         len = Strlen(s = dp->di_name) + 1;
  249.  
  250.     cur += len;
  251.     if ((dflag & DIR_LINE) && cur >= T_Cols - 1 && len < T_Cols) {
  252.         xputchar('\n');
  253.         cur = len;
  254.     }
  255.     xprintf(s != dp->di_name ? "~%S%c" : "%S%c",
  256.         s, (dflag & DIR_VERT) ? '\n' : ' ');
  257.     } while ((dp = dp->di_prev) != dcwd);
  258.     if (!(dflag & DIR_VERT))
  259.     xputchar('\n');
  260. }
  261.  
  262. void
  263. dtildepr(home, dir)
  264.     register Char *home, *dir;
  265. {
  266.  
  267.     if (!eq(home, STRslash) && prefix(home, dir))
  268.     xprintf("~%S", dir + Strlen(home));
  269.     else
  270.     xprintf("%S", dir);
  271. }
  272.  
  273. void
  274. dtilde()
  275. {
  276.     struct directory *d = dcwd;
  277.  
  278.     do {
  279.     if (d == &dhead)
  280.         continue;
  281.     d->di_name = dcanon(d->di_name, STRNULL);
  282.     } while ((d = d->di_prev) != dcwd);
  283.  
  284.     dset(dcwd->di_name);
  285. }
  286.  
  287.  
  288. /* dnormalize():
  289.  *    The path will be normalized if it
  290.  *    1) is "..",
  291.  *    2) or starts with "../",
  292.  *    3) or ends with "/..",
  293.  *    4) or contains the string "/../",
  294.  *    then it will be normalized, unless those strings are quoted. 
  295.  *    Otherwise, a copy is made and sent back.
  296.  */
  297. Char   *
  298. dnormalize(cp, exp)
  299.     Char   *cp;
  300.     int exp;
  301. {
  302.  
  303. /* return true if dp is of the form "../xxx" or "/../xxx" */
  304. #define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
  305. #define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
  306.  
  307. #ifdef S_IFLNK
  308.     if (exp) {
  309.      int     dotdot = 0;
  310.     Char   *dp, *cwd, *start = cp, buf[MAXPATHLEN];
  311. # ifdef apollo
  312.     bool slashslash;
  313. # endif /* apollo */
  314.  
  315.     /*
  316.      * count the number of "../xxx" or "xxx/../xxx" in the path
  317.      */
  318.     for (dp=start; *dp && *(dp+1); dp++)
  319.         if (IS_DOTDOT(start, dp))
  320.             dotdot++;
  321.     /*
  322.      * if none, we are done.
  323.      */
  324.         if (dotdot == 0)
  325.         return (Strsave(cp));
  326.  
  327.     cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) *
  328.                      sizeof(Char)));
  329.     (void) Strcpy(cwd, dcwd->di_name);
  330.  
  331.     /*
  332.      * If the path starts with a slash, we are not relative to
  333.      * the current working directory.
  334.      */
  335.     if ( *start == '/' )
  336.         *cwd = '\0';
  337. # ifdef apollo
  338.     slashslash = cwd[0] == '/' && cwd[1] == '/';
  339. # endif /* apollo */
  340.  
  341.     /*
  342.      * Ignore . and count ..'s
  343.      */
  344.     for (;;) {
  345.         dotdot = 0;
  346.         buf[0] = '\0';
  347.         dp = buf; 
  348.         while (*cp) 
  349.             if (IS_DOT(start, cp)) {
  350.                 if (*++cp)
  351.                     cp++;
  352.             }
  353.             else if (IS_DOTDOT(start, cp)) {
  354.             if (buf[0])
  355.                 break; /* finish analyzing .././../xxx/[..] */
  356.             dotdot++;
  357.             cp += 2;
  358.             if (*cp)
  359.                 cp++;
  360.             }
  361.             else 
  362.             *dp++ = *cp++;
  363.  
  364.         *dp = '\0';
  365.         while (dotdot > 0) 
  366.             if ((dp = Strrchr(cwd, '/')) != NULL) {
  367. # ifdef apollo
  368.             if (dp == &cwd[1]) 
  369.                 slashslash = 1;
  370. # endif /* apollo */
  371.                 *dp = '\0';
  372.                 dotdot--;
  373.             }
  374.             else
  375.             break;
  376.  
  377.         if (!*cwd) {    /* too many ..'s, starts with "/" */
  378.             cwd[0] = '/';
  379. # ifdef apollo
  380.         cwd[1] = '/';
  381.         cwd[2] = '\0';
  382. # else /* !apollo */
  383.         cwd[1] = '\0';
  384. # endif /* apollo */
  385.         }
  386. # ifdef apollo
  387.         else if (slashslash && cwd[1] == '\0') {
  388.         cwd[1] = '/';
  389.         cwd[2] = '\0';
  390.         }
  391. # endif /* apollo */
  392.  
  393.         if (buf[0]) {
  394.             if ((TRM(cwd[(dotdot = Strlen(cwd)) - 1])) != '/')
  395.             cwd[dotdot++] = '/';
  396.             cwd[dotdot] = '\0';
  397.             dp = Strspl(cwd, TRM(buf[0]) == '/' ? &buf[1] : buf);
  398.             xfree((ptr_t) cwd);
  399.             cwd = dp;
  400.             if ((TRM(cwd[(dotdot = Strlen(cwd)) - 1])) == '/')
  401.             cwd[--dotdot] = '\0';
  402.         }
  403.         if (!*cp)
  404.             break;
  405.     }
  406.     return cwd;
  407.     }
  408. #endif /* S_IFLNK */
  409.     return Strsave(cp);
  410. }
  411.  
  412.  
  413. /*
  414.  * dochngd - implement chdir command.
  415.  */
  416. /*ARGSUSED*/
  417. void
  418. dochngd(v, c)
  419.     Char  **v;
  420.     struct command *c;
  421. {
  422.     register Char *cp;
  423.     register struct directory *dp;
  424.  
  425.     int dflag = skipargs(&v, "lvn", "[-|<dir>]");
  426.     printd = 0;
  427.     cp = (dflag & DIR_OLD) ? olddir : *v;
  428.  
  429.     if (cp == NULL) {
  430.     if ((cp = value(STRhome)) == STRNULL || *cp == 0)
  431.         stderror(ERR_NAME | ERR_NOHOMEDIR);
  432.     if (chdir(short2str(cp)) < 0)
  433.         stderror(ERR_NAME | ERR_CANTCHANGE);
  434.     cp = Strsave(cp);
  435.     }
  436.     else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
  437.     stderror(ERR_NAME | ERR_TOOMANY);
  438.     /* NOTREACHED */
  439.     return;
  440.     }
  441.     else if ((dp = dfind(cp)) != 0) {
  442.     char   *tmp;
  443.  
  444.     printd = 1;
  445.     if (chdir(tmp = short2str(dp->di_name)) < 0)
  446.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  447.     dcwd->di_prev->di_next = dcwd->di_next;
  448.     dcwd->di_next->di_prev = dcwd->di_prev;
  449.     dfree(dcwd);
  450.     dnewcwd(dp, dflag);
  451.     return;
  452.     }
  453.     else
  454.     if ((cp = dfollow(cp)) == NULL)
  455.         return;
  456.     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  457.     dp->di_name = cp;
  458.     dp->di_count = 0;
  459.     dp->di_next = dcwd->di_next;
  460.     dp->di_prev = dcwd->di_prev;
  461.     dp->di_prev->di_next = dp;
  462.     dp->di_next->di_prev = dp;
  463.     dfree(dcwd);
  464.     dnewcwd(dp, dflag);
  465. }
  466.  
  467. static Char *
  468. dgoto(cp)
  469.     Char   *cp;
  470. {
  471.     Char   *dp;
  472.  
  473.     if (*cp != '/') {
  474.     register Char *p, *q;
  475.     int     cwdlen;
  476.  
  477.     for (p = dcwd->di_name; *p++;)
  478.         continue;
  479.     if ((cwdlen = p - dcwd->di_name - 1) == 1)    /* root */
  480.         cwdlen = 0;
  481.     for (p = cp; *p++;)
  482.         continue;
  483.     dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char)));
  484.     for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';)
  485.         continue;
  486.     if (cwdlen)
  487.         p[-1] = '/';
  488.     else
  489.         p--;        /* don't add a / after root */
  490.     for (q = cp; (*p++ = *q++) != '\0';)
  491.         continue;
  492.     xfree((ptr_t) cp);
  493.     cp = dp;
  494.     dp += cwdlen;
  495.     }
  496.     else
  497.     dp = cp;
  498.  
  499.     cp = dcanon(cp, dp);
  500.     return cp;
  501. }
  502.  
  503. /*
  504.  * dfollow - change to arg directory; fall back on cdpath if not valid
  505.  */
  506. static Char *
  507. dfollow(cp)
  508.     register Char *cp;
  509. {
  510.     register Char *dp;
  511.     struct varent *c;
  512.     char    ebuf[MAXPATHLEN];
  513.     int serrno;
  514.  
  515.     cp = globone(cp, G_ERROR);
  516. #ifdef apollo
  517.     if (Strchr(cp, '`')) {
  518.     char *dptr, *ptr;
  519.     if (chdir(dptr = short2str(cp)) < 0) 
  520.         stderror(ERR_SYSTEM, dptr, strerror(errno));
  521.     else if ((ptr = getwd(ebuf)) && *ptr != '\0') {
  522.         xfree((ptr_t) cp);
  523.         cp = Strsave(str2short(ptr));
  524.         return dgoto(cp);
  525.     }
  526.     else 
  527.         stderror(ERR_SYSTEM, dptr, ebuf);
  528.     }
  529. #endif /* apollo */
  530.         
  531.     (void) strcpy(ebuf, short2str(cp));
  532.     /*
  533.      * if we are ignoring symlinks, try to fix relatives now.
  534.      * if we are expading symlinks, it should be done by now.
  535.      */ 
  536.     dp = dnormalize(cp, symlinks == SYM_IGNORE);
  537.     if (chdir(short2str(dp)) >= 0) {
  538.         xfree((ptr_t) cp);
  539.         return dgoto(dp);
  540.     }
  541.     else {
  542.         xfree((ptr_t) dp);
  543.         if (chdir(short2str(cp)) >= 0)
  544.         return dgoto(cp);
  545.     else if (errno != ENOENT && errno != ENOTDIR)
  546.         stderror(ERR_SYSTEM, ebuf, strerror(errno));
  547.     serrno = errno;
  548.     }
  549.  
  550.     if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
  551.     && (c = adrof(STRcdpath))) {
  552.     Char  **cdp;
  553.     register Char *p;
  554.     Char    buf[MAXPATHLEN];
  555.  
  556.     for (cdp = c->vec; *cdp; cdp++) {
  557.         for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';)
  558.         continue;
  559.         dp[-1] = '/';
  560.         for (p = cp; (*dp++ = *p++) != '\0';)
  561.         continue;
  562.         /*
  563.          * We always want to fix the directory here
  564.          * If we are normalizing symlinks
  565.          */
  566.         dp = dnormalize(buf, symlinks == SYM_IGNORE || 
  567.                  symlinks == SYM_EXPAND);
  568.         if (chdir(short2str(dp)) >= 0) {
  569.         printd = 1;
  570.         xfree((ptr_t) cp);
  571.         return dgoto(dp);
  572.         }
  573.         else if (chdir(short2str(cp)) >= 0) {
  574.         printd = 1;
  575.         xfree((ptr_t) dp);
  576.         return dgoto(cp);
  577.         }
  578.     }
  579.     }
  580.     dp = value(cp);
  581.     if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
  582.     xfree((ptr_t) cp);
  583.     cp = Strsave(dp);
  584.     printd = 1;
  585.     return dgoto(cp);
  586.     }
  587.     xfree((ptr_t) cp);
  588.     /*
  589.      * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
  590.      * directories we could get to.
  591.      */
  592.     if (!bequiet) {
  593.     stderror(ERR_SYSTEM, ebuf, strerror(serrno));
  594.     return (NULL);
  595.     }
  596.     else
  597.     return (NULL);
  598. }
  599.  
  600.  
  601. /*
  602.  * dopushd - push new directory onto directory stack.
  603.  *    with no arguments exchange top and second.
  604.  *    with numeric argument (+n) bring it to top.
  605.  */
  606. /*ARGSUSED*/
  607. void
  608. dopushd(v, c)
  609.     Char  **v;
  610.     struct command *c;
  611. {
  612.     register struct directory *dp;
  613.     register Char *cp;
  614.  
  615.     int dflag = skipargs(&v, "lvn", " [-|<dir>|+<n>]");
  616.     printd = 1;
  617.     cp = (dflag & DIR_OLD) ? olddir : *v;
  618.  
  619.     if (cp == NULL) {
  620.     if (adrof(STRpushdtohome)) {
  621.         if ((cp = value(STRhome)) == STRNULL || *cp == 0)
  622.         stderror(ERR_NAME | ERR_NOHOMEDIR);
  623.         if (chdir(short2str(cp)) < 0)
  624.         stderror(ERR_NAME | ERR_CANTCHANGE);
  625.         cp = Strsave(cp);    /* hmmm... PWP */
  626.         if ((cp = dfollow(cp)) == NULL)
  627.         return;
  628.         dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  629.         dp->di_name = cp;
  630.         dp->di_count = 0;
  631.         dp->di_prev = dcwd;
  632.         dp->di_next = dcwd->di_next;
  633.         dcwd->di_next = dp;
  634.         dp->di_next->di_prev = dp;
  635.     }
  636.     else {
  637.         char   *tmp;
  638.  
  639.         if ((dp = dcwd->di_prev) == &dhead)
  640.         dp = dhead.di_prev;
  641.         if (dp == dcwd)
  642.         stderror(ERR_NAME | ERR_NODIR);
  643.         if (chdir(tmp = short2str(dp->di_name)) < 0)
  644.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  645.         dp->di_prev->di_next = dp->di_next;
  646.         dp->di_next->di_prev = dp->di_prev;
  647.         dp->di_next = dcwd->di_next;
  648.         dp->di_prev = dcwd;
  649.         dcwd->di_next->di_prev = dp;
  650.         dcwd->di_next = dp;
  651.     }
  652.     }
  653.     else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
  654.     stderror(ERR_NAME | ERR_TOOMANY);
  655.     /* NOTREACHED */
  656.     return;
  657.     }
  658.     else if ((dp = dfind(cp)) != NULL) {
  659.     char   *tmp;
  660.  
  661.     if (chdir(tmp = short2str(dp->di_name)) < 0)
  662.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  663.     /*
  664.      * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
  665.      */
  666.     if (adrof(STRdextract))
  667.         dextract(dp);
  668.     }
  669.     else {
  670.     register Char *ccp;
  671.  
  672.     if ((ccp = dfollow(cp)) == NULL)
  673.         return;
  674.     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  675.     dp->di_name = ccp;
  676.     dp->di_count = 0;
  677.     dp->di_prev = dcwd;
  678.     dp->di_next = dcwd->di_next;
  679.     dcwd->di_next = dp;
  680.     dp->di_next->di_prev = dp;
  681.     }
  682.     dnewcwd(dp, dflag);
  683. }
  684.  
  685. /*
  686.  * dfind - find a directory if specified by numeric (+n) argument
  687.  */
  688. static struct directory *
  689. dfind(cp)
  690.     register Char *cp;
  691. {
  692.     register struct directory *dp;
  693.     register int i;
  694.     register Char *ep;
  695.  
  696.     if (*cp++ != '+')
  697.     return (0);
  698.     for (ep = cp; Isdigit(*ep); ep++)
  699.     continue;
  700.     if (*ep)
  701.     return (0);
  702.     i = getn(cp);
  703.     if (i <= 0)
  704.     return (0);
  705.     for (dp = dcwd; i != 0; i--) {
  706.     if ((dp = dp->di_prev) == &dhead)
  707.         dp = dp->di_prev;
  708.     if (dp == dcwd)
  709.         stderror(ERR_NAME | ERR_DEEP);
  710.     }
  711.     return (dp);
  712. }
  713.  
  714. /*
  715.  * dopopd - pop a directory out of the directory stack
  716.  *    with a numeric argument just discard it.
  717.  */
  718. /*ARGSUSED*/
  719. void
  720. dopopd(v, c)
  721.     Char  **v;
  722.     struct command *c;
  723. {
  724.     Char *cp;
  725.     register struct directory *dp, *p = NULL;
  726.  
  727.     int dflag = skipargs(&v, "lvn", " [-|+<n>]");
  728.     printd = 1;
  729.     cp = (dflag & DIR_OLD) ? olddir : *v;
  730.  
  731.     if (cp == NULL)
  732.     dp = dcwd;
  733.     else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
  734.     stderror(ERR_NAME | ERR_TOOMANY);
  735.     /* NOTREACHED */
  736.     return;
  737.     }
  738.     else if ((dp = dfind(cp)) == 0)
  739.     stderror(ERR_NAME | ERR_BADDIR);
  740.     if (dp->di_prev == &dhead && dp->di_next == &dhead)
  741.     stderror(ERR_NAME | ERR_EMPTY);
  742.     if (dp == dcwd) {
  743.     char   *tmp;
  744.  
  745.     if ((p = dp->di_prev) == &dhead)
  746.         p = dhead.di_prev;
  747.     if (chdir(tmp = short2str(p->di_name)) < 0)
  748.         stderror(ERR_SYSTEM, tmp, strerror(errno));
  749.     }
  750.     dp->di_prev->di_next = dp->di_next;
  751.     dp->di_next->di_prev = dp->di_prev;
  752.     if (dp == dcwd)
  753.     dnewcwd(p, dflag);
  754.     else {
  755.     printdirs(dflag);
  756.     }
  757.     dfree(dp);
  758. }
  759.  
  760. /*
  761.  * dfree - free the directory (or keep it if it still has ref count)
  762.  */
  763. void
  764. dfree(dp)
  765.     register struct directory *dp;
  766. {
  767.  
  768.     if (dp->di_count != 0) {
  769.     dp->di_next = dp->di_prev = 0;
  770.     }
  771.     else {
  772.     xfree((ptr_t) dp->di_name);
  773.     xfree((ptr_t) dp);
  774.     }
  775. }
  776.  
  777. /*
  778.  * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
  779.  *    we are of course assuming that the file system is standardly
  780.  *    constructed (always have ..'s, directories have links)
  781.  */
  782. Char   *
  783. dcanon(cp, p)
  784.     register Char *cp, *p;
  785. {
  786.     register Char *sp;
  787.     register Char *p1, *p2;    /* general purpose */
  788.     bool    slash;
  789. #ifdef apollo
  790.     bool    slashslash;
  791. #endif /* apollo */
  792.  
  793. #ifdef S_IFLNK            /* if we have symlinks */
  794.     Char    link[MAXPATHLEN];
  795.     char    tlink[MAXPATHLEN];
  796.     int     cc;
  797.     Char   *newcp;
  798. #endif /* S_IFLNK */
  799.  
  800.     /*
  801.      * christos: if the path given does not start with a slash prepend cwd. If
  802.      * cwd does not start with a slash or the result would be too long abort().
  803.      */
  804.     if (*cp != '/') {
  805.     Char    tmpdir[MAXPATHLEN];
  806.  
  807.     p1 = value(STRcwd);
  808.     if (p1 == STRNULL || *p1 != '/')
  809.         abort();
  810.     if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN)
  811.         abort();
  812.     (void) Strcpy(tmpdir, p1);
  813.     (void) Strcat(tmpdir, STRslash);
  814.     (void) Strcat(tmpdir, cp);
  815.     xfree((ptr_t) cp);
  816.     cp = p = Strsave(tmpdir);
  817.     }
  818.  
  819. #ifdef COMMENT
  820.     if (*cp != '/')
  821.     abort();
  822. #endif /* COMMENT */
  823.  
  824. #ifdef apollo
  825.     slashslash = (cp[0] == '/' && cp[1] == '/');
  826. #endif /* apollo */
  827.  
  828.     while (*p) {        /* for each component */
  829.     sp = p;            /* save slash address */
  830.     while (*++p == '/')    /* flush extra slashes */
  831.         continue;
  832.     if (p != ++sp)
  833.         for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
  834.         continue;
  835.     p = sp;            /* save start of component */
  836.     slash = 0;
  837.     while (*++p)        /* find next slash or end of path */
  838.         if (*p == '/') {
  839.         slash = 1;
  840.         *p = 0;
  841.         break;
  842.         }
  843.  
  844. #ifdef apollo
  845.     if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
  846.         slashslash = 1;
  847. #endif /* apollo */
  848.     if (*sp == '\0')    /* if component is null */
  849.         if (--sp == cp)    /* if path is one char (i.e. /) */ 
  850.         break;
  851.         else
  852.         *sp = '\0';
  853.     else if (sp[0] == '.' && sp[1] == 0) {
  854.         if (slash) {
  855.         for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';)
  856.             continue;
  857.         p = --sp;
  858.         }
  859.         else if (--sp != cp)
  860.         *sp = '\0';
  861.     }
  862.     else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
  863.         /*
  864.          * We have something like "yyy/xxx/..", where "yyy" can be null or
  865.          * a path starting at /, and "xxx" is a single component. Before
  866.          * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
  867.          * symbolic link.
  868.          */
  869.         *--sp = 0;        /* form the pathname for readlink */
  870. #ifdef S_IFLNK            /* if we have symlinks */
  871.         if (sp != cp && symlinks != SYM_IGNORE &&
  872.         (cc = readlink(short2str(cp), tlink,
  873.                    sizeof tlink)) >= 0) {
  874.         (void) Strcpy(link, str2short(tlink));
  875.         link[cc] = '\0';
  876.  
  877.         if (slash)
  878.             *p = '/';
  879.         /*
  880.          * Point p to the '/' in "/..", and restore the '/'.
  881.          */
  882.         *(p = sp) = '/';
  883.         /*
  884.          * find length of p
  885.          */
  886.         for (p1 = p; *p1++;)
  887.             continue;
  888.         if (*link != '/') {
  889.             /*
  890.              * Relative path, expand it between the "yyy/" and the
  891.              * "/..". First, back sp up to the character past "yyy/".
  892.              */
  893.             while (*--sp != '/')
  894.             continue;
  895.             sp++;
  896.             *sp = 0;
  897.             /*
  898.              * New length is "yyy/" + link + "/.." and rest
  899.              */
  900.             p1 = newcp = (Char *) xmalloc((size_t)
  901.                         (((sp - cp) + cc + (p1 - p)) *
  902.                          sizeof(Char)));
  903.             /*
  904.              * Copy new path into newcp
  905.              */
  906.             for (p2 = cp; (*p1++ = *p2++) != '\0';)
  907.             continue;
  908.             for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
  909.             continue;
  910.             for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
  911.             continue;
  912.             /*
  913.              * Restart canonicalization at expanded "/xxx".
  914.              */
  915.             p = sp - cp - 1 + newcp;
  916.         }
  917.         else {
  918.             /*
  919.              * New length is link + "/.." and rest
  920.              */
  921.             p1 = newcp = (Char *) xmalloc((size_t)
  922.                         ((cc + (p1 - p)) * sizeof(Char)));
  923.             /*
  924.              * Copy new path into newcp
  925.              */
  926.             for (p2 = link; (*p1++ = *p2++) != '\0';)
  927.             continue;
  928.             for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
  929.             continue;
  930.             /*
  931.              * Restart canonicalization at beginning
  932.              */
  933.             p = newcp;
  934.         }
  935.         xfree((ptr_t) cp);
  936.         cp = newcp;
  937. #ifdef apollo
  938.                 slashslash = (cp[0] == '/' && cp[1] == '/');
  939. #endif /* apollo */
  940.         continue;    /* canonicalize the link */
  941.         }
  942. #endif /* S_IFLNK */
  943.         *sp = '/';
  944.         if (sp != cp)
  945.         while (*--sp != '/')
  946.             continue;
  947.         if (slash) {
  948.         for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
  949.             continue;
  950.         p = sp;
  951.         }
  952.         else if (cp == sp)
  953.         *++sp = '\0';
  954.         else
  955.         *sp = '\0';
  956.     }
  957.     else {            /* normal dir name (not . or .. or nothing) */
  958.  
  959. #ifdef S_IFLNK            /* if we have symlinks */
  960.         if (sp != cp && symlinks == SYM_CHASE &&
  961.         (cc = readlink(short2str(cp), tlink,
  962.                    sizeof tlink)) >= 0) {
  963.         (void) Strcpy(link, str2short(tlink));
  964.         link[cc] = '\0';
  965.  
  966.         /*
  967.          * restore the '/'.
  968.          */
  969.         if (slash)
  970.             *p = '/';
  971.  
  972.         /*
  973.          * point sp to p (rather than backing up).
  974.          */
  975.         sp = p;
  976.  
  977.         /*
  978.          * find length of p
  979.          */
  980.         for (p1 = p; *p1++;)
  981.             continue;
  982.         if (*link != '/') {
  983.             /*
  984.              * Relative path, expand it between the "yyy/" and the
  985.              * remainder. First, back sp up to the character past
  986.              * "yyy/".
  987.              */
  988.             while (*--sp != '/')
  989.             continue;
  990.             sp++;
  991.             *sp = 0;
  992.             /*
  993.              * New length is "yyy/" + link + "/.." and rest
  994.              */
  995.             p1 = newcp = (Char *) xmalloc((size_t)
  996.                           (((sp - cp) + cc + (p1 - p))
  997.                            * sizeof(Char)));
  998.             /*
  999.              * Copy new path into newcp
  1000.              */
  1001.             for (p2 = cp; (*p1++ = *p2++) != '\0';)
  1002.             continue;
  1003.             for (p1--, p2 = link; (*p1++ = *p2++) != '\0';)
  1004.             continue;
  1005.             for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
  1006.             continue;
  1007.             /*
  1008.              * Restart canonicalization at expanded "/xxx".
  1009.              */
  1010.             p = sp - cp - 1 + newcp;
  1011.         }
  1012.         else {
  1013.             /*
  1014.              * New length is link + the rest
  1015.              */
  1016.             p1 = newcp = (Char *) xmalloc((size_t)
  1017.                         ((cc + (p1 - p)) * sizeof(Char)));
  1018.             /*
  1019.              * Copy new path into newcp
  1020.              */
  1021.             for (p2 = link; (*p1++ = *p2++) != '\0';)
  1022.             continue;
  1023.             for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
  1024.             continue;
  1025.             /*
  1026.              * Restart canonicalization at beginning
  1027.              */
  1028.             p = newcp;
  1029.         }
  1030.         xfree((ptr_t) cp);
  1031.         cp = newcp;
  1032. #ifdef apollo
  1033.                 slashslash = (cp[0] == '/' && cp[1] == '/');
  1034. #endif /* apollo */
  1035.         continue;    /* canonicalize the link */
  1036.         }
  1037. #endif /* S_IFLNK */
  1038.         if (slash)
  1039.         *p = '/';
  1040.     }
  1041.     }
  1042.  
  1043.     /*
  1044.      * fix home...
  1045.      */
  1046. #ifdef S_IFLNK
  1047.     p1 = value(STRhome);
  1048.     cc = Strlen(p1);
  1049.     /*
  1050.      * See if we're not in a subdir of STRhome
  1051.      */
  1052.     if (p1 && *p1 == '/' &&
  1053.     (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) {
  1054.     static ino_t home_ino = (ino_t) -1;
  1055.     static dev_t home_dev = -1;
  1056.     static Char *home_ptr = NULL;
  1057.     struct stat statbuf;
  1058.  
  1059.     /*
  1060.      * Get dev and ino of STRhome
  1061.      */
  1062.     if (home_ptr != p1 &&
  1063.         stat(short2str(p1), &statbuf) != -1) {
  1064.         home_dev = statbuf.st_dev;
  1065.         home_ino = statbuf.st_ino;
  1066.         home_ptr = p1;
  1067.     }
  1068.     /*
  1069.      * Start comparing dev & ino backwards
  1070.      */
  1071.     p2 = Strcpy(link, cp);
  1072.     for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) {
  1073.         if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) &&
  1074.             statbuf.st_ino == home_ino) {
  1075.             sp = (Char *) - 1;
  1076.             break;
  1077.         }
  1078.         if ((sp = Strrchr(p2, '/')) != NULL)
  1079.         *sp = '\0';
  1080.     }
  1081.     /*
  1082.      * See if we found it
  1083.      */
  1084.     if (*p2 && sp == (Char *) -1) {
  1085.         /*
  1086.          * Use STRhome to make '~' work
  1087.          */
  1088.         newcp = Strspl(p1, cp + Strlen(p2));
  1089.         xfree((ptr_t) cp);
  1090.         cp = newcp;
  1091.     }
  1092.     }
  1093. #endif /* S_IFLNK */
  1094.  
  1095. #ifdef apollo
  1096.     if (slashslash) {
  1097.     if (cp[1] != '/') {
  1098.         p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char));
  1099.         *p = '/';
  1100.         (void) Strcpy(&p[1], cp);
  1101.         xfree((ptr_t) cp);
  1102.         cp = p;
  1103.     }
  1104.     }
  1105.     if (cp[1] == '/' && cp[2] == '/') 
  1106.     (void) Strcpy(&cp[1], &cp[2]);
  1107. #endif /* apollo */
  1108.     return cp;
  1109. }
  1110.  
  1111.  
  1112. /*
  1113.  * dnewcwd - make a new directory in the loop the current one
  1114.  */
  1115. static void
  1116. dnewcwd(dp, dflag)
  1117.     register struct directory *dp;
  1118.     int dflag;
  1119. {
  1120.  
  1121.     if (adrof(STRdunique)) {
  1122.     struct directory *dn;
  1123.  
  1124.     for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev) 
  1125.         if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) {
  1126.         dn->di_next->di_prev = dn->di_prev;
  1127.         dn->di_prev->di_next = dn->di_next;
  1128.         dfree(dn);
  1129.         break;
  1130.         }
  1131.     }
  1132.     dcwd = dp;
  1133.     dset(dcwd->di_name);
  1134.     dgetstack();
  1135.     if (printd && !(adrof(STRpushdsilent))    /* PWP: pushdsilent */
  1136.     && !bequiet)        /* be quite while restoring stack -strike */
  1137.     printdirs(dflag);
  1138.     cwd_cmd();            /* PWP: run the defined cwd command */
  1139. }
  1140.  
  1141. void
  1142. dsetstack()
  1143. {
  1144.     Char **cp;
  1145.     struct varent *vp;
  1146.     struct directory *dn, *dp;
  1147.  
  1148.     if ((vp = adrof(STRdirstack)) == NULL)
  1149.     return;
  1150.  
  1151.     /* Free the whole stack */
  1152.     while ((dn = dhead.di_prev) != &dhead) {
  1153.     dn->di_next->di_prev = dn->di_prev;
  1154.     dn->di_prev->di_next = dn->di_next;
  1155.     if (dn != dcwd)
  1156.         dfree(dn);
  1157.     }
  1158.  
  1159.     /* thread the current working directory */
  1160.     dhead.di_prev = dhead.di_next = dcwd;
  1161.     dcwd->di_next = dcwd->di_prev = &dhead;
  1162.  
  1163.     /* put back the stack */
  1164.     for (cp = vp->vec; cp && *cp && **cp; cp++) {
  1165.     dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
  1166.     dp->di_name = Strsave(*cp);
  1167.     dp->di_count = 0;
  1168.     dp->di_prev = dcwd;
  1169.     dp->di_next = dcwd->di_next;
  1170.     dcwd->di_next = dp;
  1171.     dp->di_next->di_prev = dp;
  1172.     }
  1173.     dgetstack();    /* Make $dirstack reflect the current state */
  1174. }
  1175.  
  1176. static void
  1177. dgetstack()
  1178. {
  1179.     if (adrof(STRdirstack)) {
  1180.     int i = 0;
  1181.     Char **dblk, **dbp;
  1182.     struct directory *dn;
  1183.  
  1184.     for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++) 
  1185.         continue;
  1186.     dbp = dblk = (Char**) xmalloc((i + 1) * sizeof(Char *));
  1187.     for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++) 
  1188.          *dbp = Strsave(dn->di_name);
  1189.     *dbp = NULL;
  1190.     setq(STRdirstack, dblk, &shvhed);
  1191.     }
  1192. }
  1193.  
  1194. /*
  1195.  * getstakd - added by kfk 17 Jan 1984
  1196.  * Support routine for the stack hack.  Finds nth directory in
  1197.  * the directory stack, or finds last directory in stack.
  1198.  */
  1199. int
  1200. getstakd(s, cnt)
  1201.     Char   *s;
  1202.     int     cnt;
  1203. {
  1204.     struct directory *dp;
  1205.  
  1206.     dp = dcwd;
  1207.     if (cnt < 0) {        /* < 0 ==> last dir requested. */
  1208.     dp = dp->di_next;
  1209.     if (dp == &dhead)
  1210.         dp = dp->di_next;
  1211.     }
  1212.     else {
  1213.     while (cnt-- > 0) {
  1214.         dp = dp->di_prev;
  1215.         if (dp == &dhead)
  1216.         dp = dp->di_prev;
  1217.         if (dp == dcwd)
  1218.         return (0);
  1219.     }
  1220.     }
  1221.     (void) Strcpy(s, dp->di_name);
  1222.     return (1);
  1223. }
  1224.  
  1225. /*
  1226.  * Karl Kleinpaste - 10 Feb 1984
  1227.  * Added dextract(), which is used in pushd +n.
  1228.  * Instead of just rotating the entire stack around, dextract()
  1229.  * lets the user have the nth dir extracted from its current
  1230.  * position, and pushes it onto the top.
  1231.  */
  1232. static void
  1233. dextract(dp)
  1234.     struct directory *dp;
  1235. {
  1236.     if (dp == dcwd)
  1237.     return;
  1238.     dp->di_next->di_prev = dp->di_prev;
  1239.     dp->di_prev->di_next = dp->di_next;
  1240.     dp->di_next = dcwd->di_next;
  1241.     dp->di_prev = dcwd;
  1242.     dp->di_next->di_prev = dp;
  1243.     dcwd->di_next = dp;
  1244. }
  1245.  
  1246. void
  1247. loaddirs(fname)
  1248.     Char *fname;
  1249. {
  1250.     static Char *loaddirs_cmd[] = { STRsource, NULL, NULL };
  1251.  
  1252.     bequiet = 1;
  1253.     if (fname) 
  1254.     loaddirs_cmd[1] = fname;
  1255.     else if ((fname = value(STRdirsfile)) != STRNULL)
  1256.     loaddirs_cmd[1] = fname;
  1257.     else
  1258.     loaddirs_cmd[1] = STRtildotdirs;
  1259.     dosource(loaddirs_cmd, (struct command *)0);
  1260.     bequiet = 0;
  1261. }
  1262.  
  1263. /*
  1264.  * create a file called ~/.cshdirs which has a sequence
  1265.  * of pushd commands which will restore the dir stack to
  1266.  * its state before exit/logout. remember that the order
  1267.  * is reversed in the file because we are pushing.
  1268.  * -strike
  1269.  */
  1270. void
  1271. recdirs(fname)
  1272.     Char *fname;
  1273. {
  1274.     int     fp, ftmp, oldidfds;
  1275.     int     cdflag = 0;
  1276.     extern int fast;
  1277.     Char    buf[BUFSIZE];
  1278.  
  1279.     if (!fast) {
  1280.     if (!adrof(STRsavedirs))/* does it exist */
  1281.         return;
  1282.     if (fname == NULL)
  1283.         if ((fname = value(STRdirsfile)) == STRNULL) {
  1284.         fname = Strcpy(buf, value(STRhome));
  1285.         (void) Strcat(buf, &STRtildotdirs[1]);
  1286.         }
  1287.         
  1288.     if ((fp = creat(short2str(fname), 0600)) == -1)
  1289.         return;
  1290.     oldidfds = didfds;
  1291.     didfds = 0;
  1292.     ftmp = SHOUT;
  1293.     SHOUT = fp;
  1294.     {
  1295.         extern struct directory *dcwd;
  1296.         struct directory *dp = dcwd->di_next;
  1297.  
  1298.         do {
  1299.         if (dp == &dhead)
  1300.             continue;
  1301.         if (cdflag == 0)
  1302.             cdflag++, xprintf("cd %s\n",
  1303.                       short2str(dp->di_name));
  1304.         else
  1305.             xprintf("pushd %s\n",
  1306.                 short2str(dp->di_name));
  1307.         } while ((dp = dp->di_next) != dcwd->di_next);
  1308.     }
  1309.     (void) close(fp);
  1310.     SHOUT = ftmp;
  1311.     didfds = oldidfds;
  1312.     }
  1313. }
  1314.  
  1315.  
  1316.